package com.lakeworks.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lakeworks.common.core.domain.AjaxResult;
import com.lakeworks.common.domain.channel.RiskVo;
import com.lakeworks.common.domain.channel.ticket.BarcodeVo;
import com.lakeworks.common.domain.system.OprProductImg;
import com.lakeworks.common.enums.MaterialType;
import com.lakeworks.common.enums.OprProductType;
import com.lakeworks.common.enums.ticket.*;
import com.lakeworks.common.utils.StringUtils;
import com.lakeworks.domain.*;
import com.lakeworks.common.domain.ticket.*;
import com.lakeworks.common.utils.DateUtils;
import com.lakeworks.decision.base.Decision;
import com.lakeworks.decision.base.DecisionHandler;
import com.lakeworks.domain.vo.TicketBackVo;
import com.lakeworks.event.*;
import com.lakeworks.mapper.*;
import com.lakeworks.processor.*;
import com.lakeworks.service.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import static com.lakeworks.common.utils.SecurityUtils.getUserName;
import static java.util.Arrays.stream;
import static org.springframework.http.ResponseEntity.status;


/**
 * 工单Service业务层处理
 *
 * @author lake
 * @date 2023-07-17
 */
@Service
public class TktTicketServiceImpl implements ITktTicketService {
    @Autowired
    private TktTicketMapper tktTicketMapper;

    @Autowired
    private ITktProcessService tktProcessService;

    @Autowired
    private ITktDecisionService tktDecisionService;

    @Autowired
    private TktTicketFileMapper tktTicketFileMapper;

    @Autowired
    private ITktNodeDecisionsService tktNodeDecisionsService;

    @Autowired
    private IOprProductService oprProductService;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private TktClaimsMapper tktClaimsMapper;

    @Autowired
    private IOprPickupService oprPickupService;

    ///////////////////////////////////

    @Autowired
    private AfterSaleProcessor afterSaleProcessor;

    @Autowired
    private BackTicketProcessor backTicketProcessor;

    @Autowired
    private UpdateTicketProcessor updateTicketProcessor;

    @Autowired
    private NotreachedProcessor notreachedProcessor;

    private static final Logger log = LoggerFactory.getLogger(TktTicketServiceImpl.class);



    @Override
    //@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public AjaxResult addTktTicket(TktTicket tktTicket) {
        log.info("打印请求参数--方法::addTktTicket------param:[{}]",tktTicket.toString());
        TktProcess process = afterSaleProcessor.apply(new AfterSaleEvent(tktTicket, getUserName()));
        tktTicket.setChannelId(getChannelId(tktTicket.getOrderNo()));
        tktTicketMapper.insertTktTicket(tktTicket);
        process.setTicketId(tktTicket.getId());
        tktProcessService.insertTktProcess(process);
        addFile(tktTicket, process.getId());
        runDecision(process.getDecisions(), process.getId().intValue());
        return AjaxResult.success();
    }

    @Override
    public AjaxResult ticketDatil(long ticketId) {
        log.info("打印请求参数--方法::ticketDatil------param:[{}]",ticketId);
        TktTicket ticket = tktTicketMapper.selectTktTicketById(ticketId);
        if (ticket == null) {
            return AjaxResult.error("没有找到工单信息");
        }
        TicketDetail build = TicketDetail.builder().build();
        BeanUtils.copyProperties(ticket, build);
        TktTicket tktTicket = tktTicketMapper.selectTktTicketByRelevanceId(ticket.getId());
        if(tktTicket != null){
            build.setRelevanceId(tktTicket.getChannelTicketId());
        }
        List<TktProcess> processes = tktProcessService.selectTktProcessList(TktProcess.builder().ticketId(ticketId).build());
        processes.forEach(p -> {
            List<TktTicketFile> files = tktTicketFileMapper.selectFileByProcessId(p.getId());
            if (!CollectionUtils.isEmpty(files)) {
                p.setRemark(files.get(0).getRemark());
                p.setFiles(files.stream().map(TktTicketFile::getFileUrl).collect(Collectors.toList()));
            }
        });
        int count = build.getNodeId().intValue() - 1016;
        if(count > 0 && count < 3){
            build.setCount(count);
        }else {
            build.setCount(0);
        }
        if(ticket.getType().equals(FlowType.MAJOR_RISK.name())){
            if(StringUtils.isNotBlank(ticket.getBarcode())){
                String[] barcodes = ticket.getBarcode().split(",");
                processes.add(TktProcess.builder().riskInfos(stream(barcodes).toList().stream().map(b->{
                    OprProduct product = oprProductService.queryByBarcode(b);
                    List<OprProductImg> imgs = oprProductService.selectImgByProductId(Optional.ofNullable(product).map(OprProduct::getId).orElse(null));
                    return RiskInfo.builder()
                            .barcode(b)
                            .riskVoList(imgs.stream().filter(i-> StringUtils.isNotBlank(i.getRemark())).map(i-> RiskVo.builder().img(i.getImgUrl()).remark(i.getRemark()).build()).collect(Collectors.toList()))
                            .build();
                }).collect(Collectors.toList())).build());
            }
        }else if(ticket.getType().equals(FlowType.PREMIUM_ADJUSTMENT.name())){
            if(StringUtils.isNotBlank(ticket.getBarcode())){
                String[] barcodes = ticket.getBarcode().split(",");
                processes.add(TktProcess.builder().materials(stream(barcodes).toList().stream().map(b->{
                    OprProduct product = oprProductService.queryByBarcode(b);
                    return MaterialInfo.builder()
                            .barcode(product.getBarcode())
                            .material(stream(MaterialType.explode(product.getFlag())).toList().stream().map(MaterialType::toString).collect(Collectors.joining(",")))
                            .build();
                }).collect(Collectors.toList())).build());
            }
        }
        build.setProcesses(processes);
        return AjaxResult.success(build);
    }

    @Override
    public PageInfo<TktTicket> selectTktTicketList(TktTicket tktTicket) {
        PageHelper.startPage(tktTicket.getPageNum(),tktTicket.getPageSize());
        List<TktTicket> tickets = tktTicketMapper.selectTktTicketList(tktTicket);
        PageInfo<TktTicket> pageInfo = new PageInfo<>(tickets);
        List<TktTicket> list = tickets.stream()
                .peek(t -> {
                    t.setDuration(DateUtils.getDatePoor(t.getModifyAt(), DateUtils.getNowDate()));
                    t.setTotalDuration(DateUtils.getDatePoor(t.getCreateAt(), DateUtils.getNowDate()));
                    if(t.getStatus().equals(TicketStatus.FINISHED.name())){
                        t.setDuration(null);
                        t.setTotalDuration(DateUtils.getDatePoor(t.getCreateAt(),t.getModifyAt()));
                    }
                    List<TktProcess> processes = tktProcessService.selectTktProcessList(TktProcess.builder().ticketId(t.getId()).build());
                    if (!CollectionUtils.isEmpty(processes)) {
                        t.setUpdateTime(processes.get(0).getCreateAt());
                    }
                })
                .collect(Collectors.toList());
        pageInfo.setList(list);
        return pageInfo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public AjaxResult updateTicket(TicketBackVo ticketBackVo) {
        log.info("打印请求参数--方法::updateTicket------param:[{}]",ticketBackVo.toString());
        TktTicket ticket = tktTicketMapper.selectTktTicketById(ticketBackVo.getTicketId());
        if (ticket == null) {
            return AjaxResult.error("工单信息错误");
        }
        ticket.setRemark(ticketBackVo.getRemark());
        //tktTicketMapper.updateTktTicket(tktTicket);
        TktProcess process = updateTicketProcessor.apply(new UpdateTicketEvent(ticket, getUserName()));
        tktProcessService.insertTktProcess(process);
        ticket.setTktTicketFiles(ticketBackVo.getTktTicketFiles());
        addFile(ticket, process.getId());
        runDecision(process.getDecisions(), process.getId().intValue());
        return AjaxResult.success();
    }

    @Override
    public AjaxResult notreached(NotreachedEvent event) {
        log.info("打印请求参数--方法::notreached------param:[{}]",event.toString());
        TktTicket ticket = tktTicketMapper.selectTktTicketById(event.getTicketId());
        if (ticket == null) {
            return AjaxResult.error("没有找到工单信息");
        }
        if(NodeType.QUANTITY_MISMATCH_FOUR.equals(ticket.getNodeId())){
           return backTicket(TicketBackVo.builder()
                   .ticketId(ticket.getId())
                   .remark(event.getRemark())
                   .tktTicketFiles(event.getTktTicketFiles())
                   .ticketBackState(TicketBackStatus.NOTREACHED.name())
                   .build());
        }
        ticket.setTktTicketFiles(event.getTktTicketFiles());
        ticket.setRemark(event.getRemark());
        event.setTicket(ticket);
        event.setOperator(getUserName());
        TktProcess process = notreachedProcessor.apply(event);
        tktTicketMapper.updateTktTicket(ticket);
        tktProcessService.insertTktProcess(process);
        addFile(ticket, process.getId());
        runDecision(process.getDecisions(), process.getId().intValue());
        return AjaxResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult backTicket(TicketBackVo ticketBackVo) {
        log.info("打印请求参数--方法::backTicket------param:[{}]",ticketBackVo.toString());
        TktTicket ticket = tktTicketMapper.selectTktTicketById(ticketBackVo.getTicketId());
        if (ticket == null) {
            return AjaxResult.error("没有找到工单信息");
        }
        ticketBackVo.setOperator(getUserName());
        ticket.setRemark(ticketBackVo.getRemark());
        TktProcess process = backTicketProcessor
                .apply(new BackTicketEvent(ticket,
                        TicketBackStatus.valueOf(ticketBackVo.getTicketBackState()),
                        ticketBackVo,
                        getUserName()));
        tktProcessService.insertTktProcess(process);
        addFile(ticket, process.getId());
        runDecision(process.getDecisions(), process.getId().intValue());
        return AjaxResult.success();
    }


    @Override
    public AjaxResult getOptions(Long nodeId, Long ticketId) {
        log.info("打印请求参数--方法::getOptions------nodeId:[{}],ticketId:[{}]",nodeId,ticketId);
        List<TktNodeDecisions> options = new ArrayList<>();
        options.add(TktNodeDecisions.builder()
                .type(TicketBackStatus.UPDATETICKET.name())
                .typeName("更新工单").build());
        TktTicket ticket = tktTicketMapper.selectTktTicketById(ticketId);
        if (ticket == null) {
            return AjaxResult.error("工单信息错误");
        }
        if(NodeType.QUANTITY_MISMATCH_THREE.equals(nodeId) || NodeType.QUANTITY_MISMATCH_FOUR.equals(nodeId)){
            nodeId = NodeType.QUANTITY_MISMATCH_ONE.getValue();
        }
        if (ticket.getStatus().equals(TicketStatus.WORKING.name())) {
            options.addAll(tktNodeDecisionsService.selectDecByNodeId(nodeId));
        }
        return AjaxResult.success(options);
    }

    /*@Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseEntity addPlatform(TicketFactory ticketFactory) {
        TktFlow flow = tktFlowMapper.selectTktFlowByName(ticketFactory.getType());
        TktRoute route = new TktRoute();
        if (flow == null) {
            return status(HttpStatus.BAD_REQUEST).build();
        }
        TktProcess process = advanceTicketProcessor.apply(new AdvanceTicketEvent(ticketFactory,thirdChannelService.getSysChannel().getName()));
        TktTicket ticket = process.getTicket();
        ticket.setFlowId(flow.getId());
        if (ticketFactory.getEventType().equals(RouteKey.TICKETNEW.name()) &&
                ticketFactory.getType().equals(RouteKey.URGENTORDER.name())) {
            route = tktRouteMapper.selectTktRouteByIKey(RouteKey.URGENTORDER.name());
        } else {
            route = tktRouteMapper.selectTktRouteByIKey(ticketFactory.getEventType());
        }
        if (route == null) {
            return status(HttpStatus.BAD_REQUEST).build();
        }
        ticket.setNodeId(route.getNodeId());
        ticket.setChannelTicketId(ticketFactory.getTicketId());
        TktTicket tktTicket = tktTicketMapper.selectTktTicketBychannelId(ticket.getChannelTicketId());
        if (tktTicket == null) {
            tktTicketMapper.insertTktTicket(ticket);
        } else {
            tktTicket.setStatus(ticket.getStatus());
            tktTicket.setNodeId(ticket.getNodeId());
            ticket.setId(tktTicket.getId());
            tktTicketMapper.updateTktTicket(tktTicket);
        }
        process.setTicketId(ticket.getId());
        process.setNodeId(route.getNodeId());
        tktProcessMapper.insertTktProcess(process);
        addFile(ticket, process.getId());
        return status(HttpStatus.NO_CONTENT).build();
    }*/



    @Override
    public AjaxResult getTicketByOrderNo(String orderNo) {
        return AjaxResult.success(tktTicketMapper.selectTicketByOrderNo(orderNo));
    }




    @Override
    public void runDecision(List<Decision> list, Integer processId) {
        list.forEach(d -> {
            DecisionHandler handler = applicationContext.getBean(d.getType().getImplementation());
            TktDecision decision = handler.apply(d);
            decision.setProcessId(processId);
            tktDecisionService.insertTktDecision(decision);
        });
    }

    @Override
    public void addFile(TktTicket tktTicket, Long processId) {
        if (!CollectionUtils.isEmpty(tktTicket.getTktTicketFiles())) {
            tktTicket.getTktTicketFiles().forEach(f -> {
                f.setTicketId(tktTicket.getId());
                f.setProcessId(processId);
                f.setCreateBy(tktTicket.getCreateBy());
                f.setModifyBy(tktTicket.getModifyBy());
                tktTicketFileMapper.insertTktTicketFile(f);
            });
        }
    }

    @Override
    public AjaxResult getClaims(Long ticketId) {
        return AjaxResult.success(tktClaimsMapper.selectTktClaimsList(TktClaims.builder().ticketId(ticketId).build()));
    }

    @Override
    public int updateWashMode(List<BarcodeVo> barcodeVos) {
        AtomicInteger num = new AtomicInteger();
        if(!CollectionUtils.isEmpty(barcodeVos)){
            barcodeVos.forEach(b->{
                OprProduct product = oprProductService.selectNoQCByBarcode(b.getBarcode());
                if(product != null){
                    OprProduct build = OprProduct.builder().barcode(b.getBarcode()).washMode(b.getWashMode()).build();
                    build.setId(product.getId());
                    if(StringUtils.isNotBlank(b.getWashMode())){
                        if (b.getWashMode().equals(ParamType.NOWASH.name())) {
                            build.setStatus(OprProductType.TOINSPECT.getValue());
                        }
                        num.addAndGet(oprProductService.updateProduct(build));
                    }
                }
            });
        }
        return num.get();
    }

    @Override
    public AjaxResult closeTicket(Long id) {
//        TktTicket ticket = tktTicketMapper.selectTktTicketBychannelId(id);
//        if(ticket == null){
//           return AjaxResult.error("没有找到这个工单");
//        }
//        ticket.setStatus(TicketStatus.FINISHED.name());
//        tktTicketMapper.updateTktTicket(ticket);
//        return AjaxResult.success(oprProductService.holdingOrder(ticket.getOrderNo(), false));
        return null;
    }

    @Override
    public TktTicket selectTktTicketBychannelId(Long channelId, Long channelTicketId) {
        return tktTicketMapper.selectTktTicketBychannelId(channelId,channelTicketId);
    }

    @Override
    public int insertTktTicket(TktTicket ticket) {
        return tktTicketMapper.insertTktTicket(ticket);
    }

    @Override
    public int updateTktTicket(TktTicket tktTicket) {
        return tktTicketMapper.updateTktTicket(tktTicket);
    }


    public Long getChannelId(String orderNo){
        List<OprPickup> pickups = oprPickupService.selectOprPickupList(OprPickup.builder().orderNo(orderNo).build());
        if(!CollectionUtils.isEmpty(pickups)){
            return pickups.get(0).getChannelId();
        }
        return null;
    }

    @Override
    public TktTicket selectTktTicketById(Long id) {
        return tktTicketMapper.selectTktTicketById(id);
    }

    @Override
    public Map<String, Long> getTimeoutNum() {
        Map<String,Long> map = new HashMap<>();
        List<TktTicket> tickets = tktTicketMapper.selectTktTicketList(TktTicket.builder().status(TicketStatus.WORKING.name()).build());
        map.put("oneDay",tickets.stream().filter(t-> DateUtils.differentDaysByMillisecond(t.getModifyAt(), DateUtils.getNowDate()) >= 1).count());
        map.put("twoDay",tickets.stream().filter(t-> DateUtils.differentDaysByMillisecond(t.getModifyAt(), DateUtils.getNowDate()) >= 2).count());
        return map;
    }

}
